package org.odefu.flash.display
{
	import starling.display.MovieClip;
	import starling.events.Event;
	import starling.textures.Texture;
	
	public final class OdefuMovieClip extends MovieClip
	{
		private const frameScripts:Vector.<Function> = new Vector.<Function>();
		
		private var _frameLabels:Array;
		
		private var textures:Vector.<Texture>;
		
		public function OdefuMovieClip(textures:Vector.<Texture>, frameLabels:Array = null, 
									   fps:int = 12)
		{
			super(textures, fps);
			
			this.textures = textures;
			
			_frameLabels = frameLabels;
			frameScripts.length = _frameLabels.length;
			
			addEventListener(Event.ADDED_TO_STAGE, addedToStage);
		}
		
		public override function dispose():void
		{
			removeEventListeners();
			
			frameScripts.splice(0, frameScripts.length);
			
			if (textures.length > 0)
			{
				for each (var texture:Texture in textures);
				{
					texture.dispose();
					texture.base.dispose();
				}
				textures.splice(0, textures.length);
			}
			
			super.dispose();
		}
		
		/**
		 * Moves the current frame to the first frame with the given name or index.
		 * Throws an Error if no such frame is found.
		 * 
		 * @param frame - the name of the frame or its index (String or int)
		 */ 
		public function gotoAndPlay(label:*):void
		{
			const index:int = label is String ? indexOfLabel(label) : label;
			if (index != -1)
			{
				currentFrame = index;
				play(); // Make sure it's playing
				return;
			}
			throw new Error("No such frame label: " + label + " in clip: " + name);
		}
		
		/**
		 * Moves the current frame to the first frame with the given name or index.
		 * Throws an Error if no such frame is found.
		 * 
		 * @param frame - the name of the frame or its index (String or int)
		 */ 
		public function gotoAndStop(label:*):void
		{
			const index:int = label is String ? indexOfLabel(label) : label;
			if (index != -1)
			{
				currentFrame = index;
				pause(); // Make sure it's not playing
				return;
			}
			throw new Error("No such frame label: " + label + " in clip: " + name);
		}
		
		/**
		 * Gets the frame label at the given index. Returns null if there is none.
		 */
		public function frameLabel(index:int):String
		{
			if (index >= 0 && index < _frameLabels.length)
			{
				return _frameLabels[index];
			}
			return null;
		}
		
		/**
		 * Returns the first index of the given label, -1 if not found.
		 */
		public function indexOfLabel(label:String):int
		{
			return _frameLabels.indexOf(label);
		}
		
		/**
		 * Returns a copy of the frame label array.
		 */
		public function get frameLabels():Array
		{
			return _frameLabels.slice();
		}
		
		/**
		 * Set a frame script to the frame specified by the given index. Use null if you want to clear a previous
		 * script.
		 */
		public function setFrameScript(index:int, func:Function):Boolean
		{
			if (index >= 0 && index < frameScripts.length)
			{
				frameScripts[index] = func;
				return true;
			}
			return false;
		}
		
		/**
		 * This method will be called every frame as long as the movieclip is on stage and playing.
		 * Remember to call super.enterFrame(event), otherwise frame scripts wont work.
		 */
		protected function enterFrame(event:Event):void
		{
			if (frameScripts[currentFrame] != null)
			{
				frameScripts[currentFrame]();
			}
		}
		
		private function addedToStage(event:Event):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, addedToStage);
			addEventListener(Event.REMOVED_FROM_STAGE, removedFromStage);
			addEventListener(Event.ENTER_FRAME, enterFrame);
		}
		
		private function removedFromStage(event:Event):void
		{
			addEventListener(Event.ADDED_TO_STAGE, addedToStage);
			removeEventListener(Event.REMOVED_FROM_STAGE, removedFromStage);
			removeEventListener(Event.ENTER_FRAME, enterFrame);
		}
	}
}